package vue.panneau2.modele;

import java.awt.Image;
import java.util.ArrayList;
import java.util.List;

import modele.Equation2D;
import modele.Equation3D;
import modele.FonctionAOptimiser2D;
import modele.FonctionAOptimiser3D;

import vue.panneau2.Zone2;
import vue.panneau2.controle.NouveauResultatPointListener;
import vue.panneau2.modele.exception.FonctionObjectifDejaParametreeException;
import vue.panneau2.modele.exception.IndiceIncorrectException;
import vue.panneau2.modele.exception.MauvaiseDimensionException;
import vue.panneau2.modele.util.Equation;
import vue.panneau2.modele.util.Simplexe;

public class ModelePanneau2 {
	/**
	 * Le probleme de programmation lineaire qui sera resolu par la methode du simplexe.
	 */
	private Simplexe problemeLineaire;
	/**
	 * Le numero d'etape courant dans la progression de l'algorithme
	 */
	private int numEtapeCourant = -1;
	private Zone2 z2;
	
	public ModelePanneau2(Zone2 z2) {
		this.z2 = z2;
	}
	
	/**
	 * Cree un probleme lineaire qui pourra etre resolu par la methode du simplexe.
	 * @param Dimension de la matrice : fixe le nombre de variables
	 * de base que la matrice pourra contenir.
	 */
	public void creerProblemeLineaire(int dimension) {
		problemeLineaire = new Simplexe(dimension);
	}
	
	/**
	 * Cree un probleme lineaire qui pourra etre resolu par la methode du simplexe.
	 * On nomme ici directement les variables par l'intermediaire de la collection
	 * nomVariables.
	 * @param Dimension de la matrice : fixe le nombre de variables
	 * de base que la matrice pourra contenir.
	 * @param nomVariables Une collection de noms indiquant les noms attribues
	 * aux variables de base.
	 */
	public void creerProblemeLineaire(int dimension, ArrayList<String> nomVariables) {
		problemeLineaire = new Simplexe(dimension, nomVariables);
	}
	
	/**
	 * Permet de recuperer le probleme courant de programmation lineaire.
	 * @return Le probleme de programmation lineaire courant encapsule dans
	 * un objet de type Simplexe.
	 */
	public Simplexe getProblemeLineaire() {
		return problemeLineaire;
	}
	
	/**
	 * Permet de mettre en place un systeme d'equation a deux dimensions
	 * a partir d'une liste d'equations 2D et d'une fonction a optimiser 2D
	 * passee en parametre.
	 * Assure leur conversion vers le systeme d'equations generiques propre a
	 * l'algorithme du simplexe.
	 * @param listeEquation2D La liste d'equations 2D qui sera mise en place pour une
	 * resolution ulterieure par la methode du simplexe.
	 * @param fctAOptimiser La fonction a optimiser ulterieurement
	 * par l'algorithme du simplexe.
	 */
	public void setSystemeEquation2D(List<Equation2D> listeEquation2D, FonctionAOptimiser2D fctAOptimiser) {
		try 
		{
			//Creation ou reinitialisation du systeme d'equation en 2D
			creerProblemeLineaire(2);
			
			//Reglage par défaut du libellé des variables
			getProblemeLineaire().setNomVariable(0, "X1");
			getProblemeLineaire().setNomVariable(1, "X2");
			
			//Ajout de toutes les equations 2D
			if (listeEquation2D != null) {
				for (Equation2D eq : listeEquation2D) {
					Equation eqGene = null;
					
					if (eq.getOperateur().equals("<=")) eqGene = new Equation(2, false, Equation.INF_OU_EGAL);
					else if (eq.getOperateur().equals(">=")) eqGene = new Equation(2, false, Equation.SUP_OU_EGAL);
					
					eqGene.setNomEquation(eq.getLibelle());
					eqGene.setCoefficient(0, eq.getX1());
					eqGene.setCoefficient(1, eq.getX2());
					eqGene.setPartieDroite(eq.getPartieDroite());
					getProblemeLineaire().ajouterEquation(eqGene);
				}
			}
			
			//Ajout de la fonction objectif
			if (fctAOptimiser != null) {
				Equation eqGene = null;
				
				if (fctAOptimiser.getOptimiser()) eqGene = new Equation(2, true, Equation.SUP_OU_EGAL);
				else eqGene = new Equation(2, true, Equation.INF_OU_EGAL);
				
				eqGene.setCoefficient(0, fctAOptimiser.getX1());
				eqGene.setCoefficient(1, fctAOptimiser.getX2());
				getProblemeLineaire().ajouterEquation(eqGene);
			}
		}
		catch (IndiceIncorrectException e) 
		{
			e.printStackTrace();
		}
		catch (FonctionObjectifDejaParametreeException e) 
		{
			e.printStackTrace();
		}
		catch (MauvaiseDimensionException e)
		{
			e.printStackTrace();
		}
	}
	
	/**
	 * Permet de mettre en place un systeme d'equation a deux dimensions
	 * a partir d'une liste d'equations 3D et d'une fonction a optimiser 3D
	 * passee en parametre.
	 * Assure leur conversion vers le systeme d'equations generiques propre a
	 * l'algorithme du simplexe.
	 * @param listeEquation3D La liste d'equations 3D qui sera mise en place pour une
	 * resolution ulterieure par la methode du simplexe.
	 * @param fctAOptimiser La fonction a optimiser ulterieurement
	 * par l'algorithme du simplexe.
	 */
	public void setSystemeEquation3D(List<Equation3D> listeEquation3D, FonctionAOptimiser3D fctAOptimiser) {
		try 
		{
			//Creation ou reinitialisation du systeme d'equation en 3D
			creerProblemeLineaire(3);
			
			//Reglage par défaut du libellé des variables
			getProblemeLineaire().setNomVariable(0, "X1");
			getProblemeLineaire().setNomVariable(1, "X2");
			getProblemeLineaire().setNomVariable(2, "X3");
			
			//Ajout de toutes les equations 3D
			if (listeEquation3D != null) {
				for (Equation3D eq : listeEquation3D) {
					Equation eqGene = null;
					
					if (eq.getOperateur().equals("<=")) eqGene = new Equation(3, false, Equation.INF_OU_EGAL);
					else if (eq.getOperateur().equals(">=")) eqGene = new Equation(3, false, Equation.SUP_OU_EGAL);
					
					eqGene.setNomEquation(eq.getLibelle());
					eqGene.setCoefficient(0, eq.getX1());
					eqGene.setCoefficient(1, eq.getX2());
					eqGene.setCoefficient(2, eq.getX3());
					eqGene.setPartieDroite(eq.getPartieDroite());
					getProblemeLineaire().ajouterEquation(eqGene);
				}
			}
			
			//Ajout de la fonction objectif
			if (fctAOptimiser != null) {
				Equation eqGene = null;
				
				if (fctAOptimiser.getOptimiser()) eqGene = new Equation(3, true, Equation.SUP_OU_EGAL);
				else eqGene = new Equation(3, true, Equation.INF_OU_EGAL);

				eqGene.setCoefficient(0, fctAOptimiser.getX1());
				eqGene.setCoefficient(1, fctAOptimiser.getX2());
				eqGene.setCoefficient(2, fctAOptimiser.getX3());
				getProblemeLineaire().ajouterEquation(eqGene);
			}
		}
		catch (IndiceIncorrectException e) 
		{
			e.printStackTrace();
		}
		catch (FonctionObjectifDejaParametreeException e) 
		{
			e.printStackTrace();
		}
		catch (MauvaiseDimensionException e)
		{
			e.printStackTrace();
		}
	}
	
	/**
	 * Permet d'obtenir sous forme d'images la succession des étapes réalisées pendant la résolution
	 * par la méthode du simplexe.
	 * @return Une collection contenant la succession des images.
	 */
	public List<Image> getListPanneauImage() {
		return z2.getVue().getListPanneauImage();
	}
	
	/**
	 * Permet d'acceder au numero d'etape courant (au niveau du programme et de l'algorithme).
	 * @return Un entier correspondant au numero de l'etape courante.
	 */
	public int getNumEtapeCourant() {
		return numEtapeCourant;
	}

	/**
	 * Permet de changer le numero d'etape courant (au niveau du programme et de l'algorithme).
	 * @param numEtapeCourant Un entier correspondant au numero de la nouvelle etape courante.
	 */
	public void setNumEtapeCourant(int numEtapeCourant) {
		this.numEtapeCourant = numEtapeCourant;
	}
	
	/**
	 * Permet de selectionner l'étape suivante dans les itérations
	 * de la méthode du simplexe.
	 */
	public void selectionnerEtapeSuivante() {
		if (this.numEtapeCourant < (getProblemeLineaire().getnombreDeMatrices() - 1)) {
			this.numEtapeCourant++;
			z2.setLNumEtapeCourante("Etape " + (numEtapeCourant+1) + "/" + getProblemeLineaire().getnombreDeMatrices());
			changePointActif();
			z2.repaint();
		}
	}
	
	/**
	 * Permet de selectionner l'étape précédente dans les itérations
	 * de la méthode du simplexe.
	 */
	public void selectionnerEtapePrecedente() {
		if (this.numEtapeCourant >= 0) {
			this.numEtapeCourant--;
			z2.setLNumEtapeCourante("Etape " + (numEtapeCourant+1) + "/" + getProblemeLineaire().getnombreDeMatrices());
			changePointActif();
			z2.repaint();
		}
	}
	
	/**
	 * Permet de changer le point actif courant dans la zone 3.
	 */
	public void changePointActif() {
		try {
			if (numEtapeCourant == -1) {
				//Envoi notification de création d'un point non visible par l'utilisateur (pas de solution à l'étape -1).
				for (NouveauResultatPointListener lis : z2.getListeDesListeners()) {
					lis.changerResultatPoint(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY, 0,"");
				}
			}
			else
			if (numEtapeCourant >= 0 && numEtapeCourant < getProblemeLineaire().getNbSolutionBaseRealisable()) {
				ArrayList<Float> solutionBaseRealisable = getProblemeLineaire().getSolutionBaseRealisableEtape(getNumEtapeCourant());
				Float valFctObj = getProblemeLineaire().getValeurFonctionObjectifEtape(getNumEtapeCourant());
				
				//Envoi notification de création d'un nouveau point
				for (NouveauResultatPointListener lis : z2.getListeDesListeners()) {
					System.out.println(solutionBaseRealisable.get(0));
					lis.changerResultatPoint(solutionBaseRealisable.get(0), solutionBaseRealisable.get(1), numEtapeCourant," Etape " + numEtapeCourant + " Val = " + valFctObj);
				}
			}
		} catch (IndiceIncorrectException e1) {
			e1.printStackTrace();
		}
	}
	
	/**
	 * Permet de résoudre le problème de programmation linéaire courant.
	 */
	public void lancerResolution() {
		getProblemeLineaire().resoudre();
		z2.setLNumEtapeCourante("Etape 0/" + getProblemeLineaire().getnombreDeMatrices());
		z2.repaint();
	}
}
